Spring boot之JPA

Spring boot连接数据库背景知识

  • JDBC
    Java DataBase Connectivity是Java连接数据操作的原生接口。JDBC对Java程序员而言是API,对实现与数据库连接的服务提供商而言是接口模型。作为API,JDBC为程序开发提供标准的接口,并为各个数据库厂商及第三方中间件厂商实现与数据库的连接提供了标准的方法。

    一句话概括:JDBC是所有框架操作数据库必须要用的,有数据库厂商提供,但是为了方便JAVA程序员调用各个数据库,各个数据库厂商都要实现JDBC接口(提供统一的标准)

  • JPA
    JAVA Persistence API是JAVA持久化规范,是ORM框架的标准(注意,它是一种标准),主流ORM框架都实现了这个标准。Sun引入新的JPA ORM规范出于两个原因:其一,简化现有的JAVA EE和JAVA SE应用开发工作;第二,Sun希望整合ORM技术,实现统一化操作。ORM是一种思想,是插入在应用程序与JDBC API之间的一个中间层,JDBC并不能很好地支持面向对象的程序设计,ORM解决了这个问题,通过JDBC将字段高效地与对象进行映射,具体实现(ORM框架)有:hibernate,toppling,spring data jpa,open jpa等。spring data jpa是对JPA规范的再次抽象,底层还是用的实现JPA的hibernate技术。本文讲的JPA就是其具体实现:Spring data jpa。
  • hibernate
    一个标准的ORM框架,实现了JPA接口
  • mybatis
    一个持久化框架,但是不完全是一个ORM框架,不是依照JPA规范。

Spring Boot JPA

Spring Boot Jpa是Spring基于ORM框架,JPA规范的基础上封装的一套JAP应用框架,可以使开发者使用极简的代码即可以实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展。Spring Boot JPA让我们摆脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现

基本查询

基本查询也分为两种,一种是默认实现,一种是根据查询的方法来自动解析成SQL。很好很强大。

默认实现

Spring Boot Jpa默认预先生成来一些基本的CURD的方法,例如:增、删、查
首先,继承JpaRepository,看代码:

1
2
3
4
@Repository
public interface PersonRepository extends JpaRepository<Person,Long> {

}

就只是实现一个接口,PersonRepository便拥有了很多能力。如:

1
2
3
4
5
6
7
8
9
public void testMethod(Person person) {

personRepository.save(person);
personRepository.count();
personRepository.delete(person);
personRepository.deleteAll();
personRepository.findAll();
//等等,上面这些都是默认提供的。
}

是不是很爽,啥都不用写,就给你赋予了这么多操作数据库的能力。

自定义简单查询

自定义的简单查询就是根据方法名来自动生成SQL,主要的语法是:findXXBy,readXXBy,queryXXBy,countXXBy,getXXBy后面跟属性名称,看代码:

1
2
3
4
5
6
7
8
9
10
11
@Repository
public interface PersonRepository extends JpaRepository<Person,Long> {
List<Person> findByName(String name);

List<Person> findByAddress(String address);

List<Person> findByNameAndAddress(String name,String address);

}

//通过方法名就知道什么意思,能实现什么操作了

基本上SQL体系中的关键词都可以使用,如Like,IgnoreCase,OrderBy等。具体的关键字,使用方法和产生SQL如下表所示:

KeyWord Sample JPQL snippet
And findByLastnameAndFirstname Where x.lastname =?1 and x.firstname =?2
Or findByLastnameOrFirstname where x.lastname= =?1 and x.firstname =?2
Is,Equals findByFirstnameIs,findByFirstnameEquals where x.firstname = ?1
Between findByStartDateBetween where x.startDate between ?1 and ?2
LessThan findByAgeLessThan where x.age < ?1
GreaterThanEqual findByAgeGreaterThanEqual where x.age >= ?1
After findByStartDateAfter where x.startDate > ?1
IsNull findByAgeIsNull where x.age is null
Like findByFirstnameLike where x.firstname like ?1
StartingWith findByFirstnameStartingWith where x.firstname like ?1 (parameter bound with appended %)

具体更多参考官网

复杂查询

在实际开发中我们需要用到分页,删除,连表等查询的时候就需要特殊的方法或者自定义SQL

分页查询

分页查询在实际使用中非常普遍了,Spring Boot Jpa已经帮我吗实现了分页的功能,在查询方法中,需要传入一个参数 Pageable,当查询中有多个参数的时候Pageable建议作为最后一个参数传入。例子:

1
2
Page<User> findALL(Pageable pageable);
Page<User> findByUserName(String userName,Pageable pageable);

Pageable是Spring封装的分页实现类,使用的时候初始化参数为:页数,每页条数,排序规则,看代码:

1
2
3
4
5
6
7
8
9
10

@Override
public List<Person> getPersonByPage(int page, int size) {

Sort sort = new Sort(Sort.Direction.DESC, "id");
Pageable pageable = PageRequest.of(page,size,sort);//new PageRequest(page,size,sort);
Page<Person> personPage = personRepository.findAll(pageable);

return personPage.getContent();
}

限制查询

有时候我们只需要查询前N个元素,或者取前一个实体,这时候可以这样操作:

1
2
3
4
5
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);

这种操作也很常见,这样就很方便了。

自定义SQL查询

其实Spring data绝大部份的SQL都可以根据方法名定义的方式来实现,但是总有情况我不想用自带的,就是要手写SQL,它也是支持的,使用@Query注解就可以做到,若果涉及到删除或者修改再需要加上@Modifying注解,也可以根据需要添加@Transactional对事物的支持,查询超时设置等。看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Modifying
@Query("update User u set u.userName = ?1 where u.id = ?2")
int modifyByIdAndUserId(String userName, Long id);

@Transactional
@Modifying
@Query("delete from User where id = ?1")
void deleteByUserId(Long id);

@Transactional(timeout = 10)
@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);

@Query("select p from Person p where p.name=:name and p.address=:address")
List<Person> withNameAndAddressQuery(@Param("name")String Name, @Param("address")String address);

多表查询

多表查询 Spring Boot Jpa 中有两种实现方式,第一种是利用 Hibernate 的级联查询来实现,第二种是创建一个结果集的接口来接收连表查询后的结果,这里主要第二种方式。

首先需要定义一个结果集的接口类。

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface HotelSummary {

City getCity();

String getName();

Double getAverageRating();

default Integer getAverageRatingRounded() {
return getAverageRating() == null ? null : (int) Math.round(getAverageRating());
}

}

查询的方法返回类型设置为新创建的接口

1
2
3
4
5
6
7
@Query("select h.city as city, h.name as name, avg(r.rating) as averageRating "
- "from Hotel h left outer join h.reviews r where h.city = ?1 group by h")
Page<HotelSummary> findByCity(City city, Pageable pageable);

@Query("select h.name as name, avg(r.rating) as averageRating "
- "from Hotel h left outer join h.reviews r group by h")
Page<HotelSummary> findByCity(Pageable pageable);

使用:

1
2
3
4
Page<HotelSummary> hotels = this.hotelRepository.findByCity(new PageRequest(0, 10, Direction.ASC, "name"));
for(HotelSummary summay:hotels){
System.out.println("Name" +summay.getName());
}

在运行中 Spring 会给接口(HotelSummary)自动生产一个代理类来接收返回的结果,代码汇总使用 getXX的形式来获取

多数据源的支持

未完待续。。。